package fr.mywiki.business.entry.meta.datatype;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Set;

import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Element;

import fr.mywiki.business.entry.meta.DataType;
import fr.mywiki.business.resources.ResourceManager;
import fr.mywiki.business.utils.StringUtils;

public class Choice implements DataType {
  public static final String TYPE_NAME = "choice";
  
  public static final String TYPE_BUNDLE_KEY = "model.type.choice.display";
  public static final String TYPE_BUNDLE_KEY_MORE = "model.type.choice.display.morethan";
  public static final String TYPE_BUNDLE_KEY_LESS = "model.type.choice.display.lessthan";
  public static final String TYPE_BUNDLE_KEY_BETWEEN = "model.type.choice.display.between";
  public static final String TYPE_BUNDLE_KEY_AMONG = "model.type.choice.display.among";

  public static final int MIN_LINKS = 0;
  public static final int MAX_LINKS = Integer.MAX_VALUE;
  
  /* Xml attributes names */
  public static final String ATTR_MIN_LINKS = "min";
  public static final String ATTR_MAX_LINKS = "max";
  
  /* The errors */
  /** Problem of type */
  public static final String INVALID_TYPE = "model.validation.choice.badtype";
  public static final String INVALID_NUMBER_MIN = "model.validation.choice.lessmin";
  public static final String INVALID_NUMBER_MAX = "model.validation.choice.moremax";
  public static final String INVALID_OBJECT = "model.validation.choice.badelement";

  /**
   * The minimal number of links that have to be chosen. Default is 0 (which can
   * be achieved also with required=false).
   */
  protected int minLinks = MIN_LINKS;

  /**
   * The maximal number of links that have to be chosen. Default is -1 (no upper
   * limit).
   */
  protected int maxLinks = MAX_LINKS;

  /**
   * The set of objects that can be potentially linked. Default is null (nothing
   * specified, any object is ok).
   */
  protected Set potentials = null;

  /**
   * Format : "Choice [of n+/- elements] [among {...}];
   */
  public String describe(Locale locale) {
  	StringBuffer buffer = new StringBuffer();
		
		buffer.append(ResourceManager.getMessage(TYPE_BUNDLE_KEY, locale));
		
		if (minLinks > MIN_LINKS) {
			if (maxLinks < MAX_LINKS) {
				// Between ...
				buffer.append(" "+ResourceManager.getMessage(TYPE_BUNDLE_KEY_BETWEEN,
						locale, new String[] { Integer.toString(minLinks),
								Integer.toString(maxLinks) }));
			} else {
				// More than ...
				buffer.append(" "+ResourceManager.getMessage(TYPE_BUNDLE_KEY_MORE, locale,
						new String[] { Integer.toString(minLinks) }));
			}
		} else if(maxLinks<MAX_LINKS) {
			// Less than ...
			buffer.append(" "+ResourceManager.getMessage(TYPE_BUNDLE_KEY_LESS, locale,
					new String[] { Integer.toString(maxLinks) }));
		}
		
		if(potentials!=null)
			buffer.append(" "
							+ ResourceManager.getMessage(TYPE_BUNDLE_KEY_AMONG, locale,
									new String[] { StringUtils
											.toString(new ArrayList(potentials)) }));
		
		return buffer.toString();
	}
  
  public String checkValue(Object value) {
  	// The object must be a Collection
  	if(!(value instanceof Collection))
  		return INVALID_TYPE;
  	
  	Collection collection = (Collection) value;
  	
  	if (minLinks > 0 && collection.size() < minLinks)
			// The minimum number of elements is not respected
			return INVALID_NUMBER_MIN;
		if (maxLinks > -1 && collection.size() > maxLinks)
			// The maximum number of elements is not respected
			return INVALID_NUMBER_MAX;
		
		if(potentials!=null) {
			// We must check if any document of the collection is included in the list
			// of potentials.
			Iterator itElements = collection.iterator();
			while(itElements.hasNext()) {
				if(!potentials.contains(itElements.next()))
					return INVALID_OBJECT;
			}
		}
  	
		return null;
	}
  
  public String getTypeName() {
    return TYPE_NAME;
  }

  public Choice() {
    this(MIN_LINKS, MAX_LINKS, null);
  }

  public Choice(int minLinks, int maxLinks) {
    this(minLinks, maxLinks, null);
  }

  public Choice(Set potentials) {
    this(MIN_LINKS, MAX_LINKS, potentials);
  }

  public Choice(int minLinks, int maxLinks, Set potentials) {
  	setMinLinks(minLinks);
    setMaxLinks(maxLinks);
    this.potentials = potentials;
  }

  public int getMaxLinks() {
    return maxLinks;
  }

  public void setMaxLinks(int maxLinks) {
		this.maxLinks = maxLinks;
		if (maxLinks < 0)
			this.maxLinks = 0;
	}

  public int getMinLinks() {
    return minLinks;
  }

  public void setMinLinks(int minLinks) {
    this.minLinks = minLinks;
    if(minLinks < MIN_LINKS)
    	this.minLinks = MIN_LINKS;
  }

  public Set getPotentials() {
    return potentials;
  }

  public void setPotentials(Set potentials) {
    this.potentials = potentials;
  }
  
  public String getDisplayKey() {
  	return TYPE_BUNDLE_KEY;
  }

  /** TODO The potentials part is not enough */
	public void describeXml(Element elt) {
		if (getMinLinks() != MIN_LINKS)
			elt.addAttribute(ATTR_MIN_LINKS, getMinLinks() + "");
		if (getMaxLinks() != MAX_LINKS)
			elt.addAttribute(ATTR_MAX_LINKS, getMaxLinks() + "");
		
		// Add potentials
		StringBuffer buffer = new StringBuffer();
		Iterator itPotentials = potentials.iterator();
		while(itPotentials.hasNext()) {
			buffer.append(itPotentials.next().toString());
			if(itPotentials.hasNext()) {
				buffer.append(",");
			}
		}
	}

	/** TODO the potentials part is to be done */
	public void fillFromXml(Element elt) {
		try {
			Attribute attr = elt.getAttribute(ATTR_MIN_LINKS);
			if (attr != null)
				setMinLinks(attr.getIntValue());

			attr = elt.getAttribute(ATTR_MAX_LINKS);
			if (attr != null)
				setMaxLinks(attr.getIntValue());
		} catch (DataConversionException e) {
			e.printStackTrace();
		}
	}

	public Object getValue(String strVal) throws ParseException {
		return null;
	}
}